/*
 License
 
 Copyright © 2012 Kevin Kimmich
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights to 
 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 the Software, and to permit persons to whom the Software is furnished to do so, 
 subject to the following conditions:
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <sys/time.h>
#include <ctype.h>
#include <uuid/uuid.h>
#include "crcore.h"

void dump_buf(unsigned char* buf, int count)
{
	int i;
	char nums[4096];
	char chars[4096];
			
	nums[0]=0;
	chars[0]=0;
	for(i=0;i<count;i++)
	{
		char work[256];
		if(isprint(buf[i]))
			snprintf(work,256,"%c",buf[i]);
		else
			snprintf(work,256,".");
		strcat(chars,work);
			
		snprintf(work,256,"%02X",(unsigned char)buf[i]);
		strcat(nums,work);
			
		if(((i+1)%4==0))
		{
			strcat(chars," ");
			strcat(nums," ");
		}
			
		if(((i+1)%32==0))
		{
			printf("%s %s\n",chars,nums);
			chars[0]=0;
			nums[0]=0;
		}
			
		if((i+1)%(128)==0)
		{
			printf("\n");
		}
	}
	
	if((i+1)%32)
	{
		printf("%s %s\n\n",chars,nums);
	}
	
}



void set_timespec( struct timespec* now, int timeout_msec)
{
	if(timeout_msec < 0)
		return;
	
	struct timeval tvnow;
	gettimeofday(&tvnow,NULL);
	now->tv_sec = tvnow.tv_sec;
	now->tv_nsec = tvnow.tv_usec*1000;
	
	now->tv_sec += (timeout_msec / 1000);
	timeout_msec -= ((timeout_msec / 1000) * 1000);
	now->tv_nsec += (timeout_msec * 1000 * 1000);//nanoseconds
	
	if(now->tv_nsec  > 1000000000)//1B
	{
		now->tv_sec ++;
		now->tv_nsec  -= 1000000000;
	}
}

void __start_me(struct elapso* e){ gettimeofday(&e->start,NULL); }
double __stop_me(struct elapso* e)
{
	gettimeofday(&e->stop,NULL);
	double elapsed = (e->stop.tv_sec - e->start.tv_sec) + (double)(e->stop.tv_usec - e->start.tv_usec)/1.0e6;
	return elapsed;
}

void daemon_init(void)
{
	/* Our process ID and Session ID */
	pid_t pid, sid;
	
	/* Fork off the parent process */
	pid = fork();
	if (pid < 0) {
		printf("FAIL!\n");
		exit(EXIT_FAILURE);
	}
	/* If we got a good PID, then
	 we can exit the parent process. */
	if (pid > 0) {
		exit(EXIT_SUCCESS);
	}
		
	/* Change the file mode mask */
	umask(0);
	
	/* Open any logs here */        
	
	/* Create a new SID for the child process */
	sid = setsid();
	if (sid < 0) {
		/* Log the failure */
		printf("Failed to setsid()!\n");
		exit(EXIT_FAILURE);
	}
	/* Redirect standard files to /dev/null */
    freopen( "/dev/null", "r", stdin);
    freopen( "/dev/null", "w", stdout);
    freopen( "/dev/null", "w", stderr);
}

//generate a uuid string
char* uuid_string(void)
{
	uuid_t u;
	uuid_generate(u);
	char* buf = malloc(37);
	uuid_unparse(u,buf);
	return buf;
}

void conditional_free( void* ptr )
{
	if(ptr)
		free(ptr);
}

int split(char* buf, int buflen, char*** items, char* delimeter)
{
	if(buf==NULL)
	{
		*items = NULL;
		return 0;
	}
	stream s;
	stream_init(&s,UNBOUNDED);
	
	char* p;
	char* last_item = buf;
	for(p=buf;p-buf < buflen;p++)
	{
		if((*p==delimeter[0]) || (p-buf == (buflen - 1)))
		{
			int len = p - last_item;
			if(p-buf == (buflen - 1))
				len++;
			char* item = malloc( len + 1 );
			memcpy(item,last_item,len);
			item[len]=0;
			stream_from_memory(&s,&item,sizeof(char*));
			last_item = p + 1;
		}
	}
	int len=0;
	(*items) = (char**) stream_to_buffer(&s,&len);
	int num = len / sizeof(char*);
	stream_cleanup(&s);
	return num;
}

/**
 * characters used for Base64 encoding
 */  
const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/**
 * encode three bytes using base64 (RFC 3548)
 *
 * @param triple three bytes that should be encoded
 * @param result buffer of four characters where the result is stored
 */  
void _base64_encode_triple(unsigned char triple[3], char result[4])
{
    int tripleValue, i;
	
    tripleValue = triple[0];
    tripleValue *= 256;
    tripleValue += triple[1];
    tripleValue *= 256;
    tripleValue += triple[2];
	
    for (i=0; i<4; i++)
    {
		result[3-i] = BASE64_CHARS[tripleValue%64];
		tripleValue /= 64;
    }
} 

/**
 * encode an array of bytes using Base64 (RFC 3548)
 *
 * @param source the source buffer
 * @param sourcelen the length of the source buffer
 * @param target the target buffer
 * @param targetlen the length of the target buffer
 * @return 1 on success, 0 otherwise
 */  
int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen)
{
    /* check if the result will fit in the target buffer */
    if ((sourcelen+2)/3*4 > targetlen-1)
		return 0;
	
    /* encode all full triples */
    while (sourcelen >= 3)
    {
		_base64_encode_triple(source, target);
		sourcelen -= 3;
		source += 3;
		target += 4;
    }
	
    /* encode the last one or two characters */
    if (sourcelen > 0)
    {
		unsigned char temp[3];
		memset(temp, 0, sizeof(temp));
		memcpy(temp, source, sourcelen);
		_base64_encode_triple(temp, target);
		target[3] = '=';
		if (sourcelen == 1)
			target[2] = '=';
		
		target += 4;
    }
	
    /* terminate the string */
    target[0] = 0;
	
    return 1;
} 

/**
 * determine the value of a base64 encoding character
 *
 * @param base64char the character of which the value is searched
 * @return the value in case of success (0-63), -1 on failure
 */  
int _base64_char_value(char base64char)
{
    if (base64char >= 'A' && base64char <= 'Z')
		return base64char-'A';
    if (base64char >= 'a' && base64char <= 'z')
		return base64char-'a'+26;
    if (base64char >= '0' && base64char <= '9')
		return base64char-'0'+2*26;
    if (base64char == '+')
		return 2*26+10;
    if (base64char == '/')
		return 2*26+11;
    return -1;
} 

/**
 * decode a 4 char base64 encoded byte triple
 *
 * @param quadruple the 4 characters that should be decoded
 * @param result the decoded data
 * @return lenth of the result (1, 2 or 3), 0 on failure
 */  
int _base64_decode_triple(char quadruple[4], unsigned char *result)
{
    int i, triple_value, bytes_to_decode = 3, only_equals_yet = 1;
    int char_value[4];
	
    for (i=0; i<4; i++)
		char_value[i] = _base64_char_value(quadruple[i]);
	
    /* check if the characters are valid */
    for (i=3; i>=0; i--)
    {
		if (char_value[i]<0)
		{
			if (only_equals_yet && quadruple[i]=='=')
			{
				/* we will ignore this character anyway, make it something
				 * that does not break our calculations */
				char_value[i]=0;
				bytes_to_decode--;
				continue;
			}
			return 0;
		}
		/* after we got a real character, no other '=' are allowed anymore */
		only_equals_yet = 0;
    }
	
    /* if we got "====" as input, bytes_to_decode is -1 */
    if (bytes_to_decode < 0)
		bytes_to_decode = 0;
	
    /* make one big value out of the partial values */
    triple_value = char_value[0];
    triple_value *= 64;
    triple_value += char_value[1];
    triple_value *= 64;
    triple_value += char_value[2];
    triple_value *= 64;
    triple_value += char_value[3];
	
    /* break the big value into bytes */
    for (i=bytes_to_decode; i<3; i++)
		triple_value /= 256;
    for (i=bytes_to_decode-1; i>=0; i--)
    {
		result[i] = triple_value%256;
		triple_value /= 256;
    }
	
    return bytes_to_decode;
} 

/**
 * decode base64 encoded data
 *
 * @param source the encoded data (zero terminated)
 * @param target pointer to the target buffer
 * @param targetlen length of the target buffer
 * @return length of converted data on success, -1 otherwise
 */  
size_t base64_decode(char *source, unsigned char *target, size_t targetlen)
{
    char *src, *tmpptr;
    char quadruple[4], tmpresult[3];
    int i, tmplen = 3;
    size_t converted = 0;
	
    /* concatinate '===' to the source to handle unpadded base64 data */
    src = (char *)malloc(strlen(source)+5);
    if (src == NULL)
		return -1;
    strcpy(src, source);
    strcat(src, "====");
    tmpptr = src;
	
    /* convert as long as we get a full result */
    while (tmplen == 3)
    {
		/* get 4 characters to convert */
		for (i=0; i<4; i++)
		{
			/* skip invalid characters - we won't reach the end */
			while (*tmpptr != '=' && _base64_char_value(*tmpptr)<0)
				tmpptr++;
			
			quadruple[i] = *(tmpptr++);
		}
		
		/* convert the characters */
		tmplen = _base64_decode_triple(quadruple, (uint8_t*)tmpresult);
		
		/* check if the fit in the result buffer */
		if (targetlen < tmplen)
		{
			free(src);
			return -1;
		}
		
		/* put the partial result in the result buffer */
		memcpy(target, tmpresult, tmplen);
		target += tmplen;
		targetlen -= tmplen;
		converted += tmplen;
    }
	
    free(src);
    return converted;
}

const char* strendswith( const char* str1, const char* str2 )
{
	if(strlen(str2) > strlen(str1))
		return NULL;
	
	const char* p = &str1[strlen(str1)-strlen(str2)];
	return strstr(p,str2);
}